#include "vector"

#include "ogsupplyLinkSolutionPartial.h"
#include "gbatchInformation.h"

/**
 * evaluates the solution
 *
 * @return evaluation
 */
double OGSupplyLinkSolutionPartial::evaluatePartial(int jobStart, int jobEnd) {
    double partialEvaluation = -1 ;

    OGSupplyLinkSolutionPartial solsave (*this) ;

    if (!solsave.checkConstraints(jobStart, jobEnd)) {
        return -1 ;
    }

    evaluation = solsave.evaluation ;
    evaluationCustomer = solsave.evaluationCustomer ;
    evaluationTransporter = solsave.evaluationTransporter ;
    nbrBatch = solsave.nbrBatch ;
    for (int j=0;j<problem->getNP();j++)
        processingSchedule[j].setDeliveryCompletionTime(solsave.processingSchedule[j].getDeliveryCompletionTime()) ;

    double partialEvaluationTransporter = nbrBatch * problem->getTransporter(0).getDeliveryCost() ;
    double partialEvaluationCustomer = 0 ;
    for (int i=jobStart;i<=jobEnd;++i) {
        double jobcustcost = problem->getJobHoldingCost(i) ;
        double jobduedate = problem->getJobDueDate(i) ;
        double jobcomptime =  processingSchedule[i].getDeliveryCompletionTime() ;
        partialEvaluationCustomer += jobcustcost*(jobduedate-jobcomptime) ;
    }

    partialEvaluation = partialEvaluationTransporter + partialEvaluationCustomer ;

    return partialEvaluation;
}

/**
 * checks if the solution satisfies the constraints
 *
 * @return true if the constraints are satisfied
 */
bool OGSupplyLinkSolutionPartial::checkConstraints(int jobStart, int jobEnd) {
    assert (this->nbrBatch>0) ;

    // Find lastDueDate :
    double lastDueDate = -1 ;
    for (int j=0;j<problem->getNP();j++)
        if (problem->getJobDueDate(j)>lastDueDate)
            lastDueDate = problem->getJobDueDate(j) ;

    vector<BatchInformation> tabBatchInformation(this->nbrBatch) ;
    for (int b=0;b<this->nbrBatch;++b) {
        tabBatchInformation[b].completionTime = lastDueDate ;
    }

    for (int i=jobStart;i<=jobEnd;++i) {
        int batchTabIndice = processingSchedule[i].getBatchIndice() - 1 ;

        assert (batchTabIndice>=0 && batchTabIndice<this->nbrBatch) ;

        // Constraint (3) : Add the size of job i to the batch where job is is assigned
        tabBatchInformation[batchTabIndice].size += problem->getJobSize(i) ;

        // Constraint (2) : Update the completion time of the batch if the due date of the job is less
        if (tabBatchInformation[batchTabIndice].completionTime > problem->getJobDueDate(i)) {
            tabBatchInformation[batchTabIndice].completionTime = problem->getJobDueDate(i) ;
        }
    }

    double lastDeliveryDate = PLUS_INFINI ;
    for (int b=this->nbrBatch-1;b>=0;--b) {
        // Constraint (3) : capacity constraint check
        //cout << "tabBatchInformation[b].size=" << tabBatchInformation[b].size << endl ;
        assert (tabBatchInformation[b].size<=problem->getTransporter(0).getCapacity()) ;

        // Constraint (5) : the interval between two successive delivery should be greater or equal to the delivery time
        if (tabBatchInformation[b].completionTime > lastDeliveryDate-problem->getTransporter(0).getDeliveryTime()) {
            tabBatchInformation[b].completionTime = lastDeliveryDate-problem->getTransporter(0).getDeliveryTime() ;
        }
        lastDeliveryDate = tabBatchInformation[b].completionTime ;

    }

    // Constraint (4) : check if all th jobs in a batch have the same completion time as the batch
    for (int i=jobStart;i<=jobEnd;++i) {
        int batchnumber = processingSchedule[i].getBatchIndice()-1 ;

        // Constraint (2) : Update the completion time of the batch if the due date of the job is less
        if (tabBatchInformation[batchnumber].completionTime != processingSchedule[i].getDeliveryCompletionTime()) {
            processingSchedule[i].setDeliveryCompletionTime (tabBatchInformation[batchnumber].completionTime) ;
        }
    }

    return true;
}
